// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.

package macaroon

import (
	"common/macaroon/pb"
	"common/uuid"
	"crypto/rand"
	"crypto/subtle"
	"github.com/tjfoc/gmsm/sm3"
	"google.golang.org/protobuf/proto"
)

const MacaroonHeadLen = 16
const MacaroonTailLen = 32

// Macaroon is a struct that determine contextual caveats and authorization.
// 使用一种比较特别的方式做限制，如果 liences == nil, 表示无限制。否则 liences 中表示许可的
type Macaroon struct {
	head     uuid.UUID //保存 user id
	licenses [][]byte
	tail     []byte //保存sign
}

// NewUnrestricted creates Macaroon with random Head and generated Tail.
func NewUnrestricted(head uuid.UUID, secret []byte) (*Macaroon, error) {
	return NewUnrestrictedFromParts(head, secret), nil
}

// NewUnrestrictedFromParts constructs an unrestricted Macaroon from the provided head and secret.
func NewUnrestrictedFromParts(head uuid.UUID, secret []byte) *Macaroon {
	return &Macaroon{
		head: head,
		tail: sign(secret, head[:]),
	}
}

func sign(secret []byte, data []byte) []byte {
	//signer := hmac.New(sha256.New, secret)
	signer := sm3.New()
	signer.Write(secret)
	_, err := signer.Write(data)
	if err != nil {
		// Error skipped because sha256 does not return error
		panic(err)
	}

	return signer.Sum(nil)
}

// NewSecret generates cryptographically random 32 bytes.
func NewSecret() (secret []byte, err error) {
	secret = make([]byte, 32)

	_, err = rand.Read(secret)
	if err != nil {
		return nil, err
	}

	return secret, nil
}

// AddFirstPartyLicence creates signed macaroon with appended Lience.
func (m *Macaroon) AddFirstPartyLicence(c []byte) (macaroon *Macaroon, err error) {
	macaroon = m.Copy()

	macaroon.licenses = append(macaroon.licenses, c)
	macaroon.tail = sign(macaroon.tail, c)

	return macaroon, nil
}

// Validate reconstructs with all Liences from the secret and compares tails,
// returning true if the tails match.
func (m *Macaroon) Validate(secret []byte) (ok bool) {
	tail := sign(secret, m.head.Bytes())
	for _, cav := range m.licenses {
		tail = sign(tail, cav)
	}

	return subtle.ConstantTimeCompare(tail, m.tail) == 1
}

// Tails returns all ancestor tails up to and including the current tail.
func (m *Macaroon) Tails(secret []byte) [][]byte {
	tails := make([][]byte, 0, len(m.licenses)+1)
	tail := sign(secret, m.head.Bytes())
	tails = append(tails, tail)
	for _, cav := range m.licenses {
		tail = sign(tail, cav)
		tails = append(tails, tail)
	}
	return tails
}

// ValidateAndTails combines Validate and Tails to a single method.
func (m *Macaroon) ValidateAndTails(secret []byte) (bool, [][]byte) {
	tails := make([][]byte, 0, len(m.licenses)+1)
	tail := sign(secret, m.head.Bytes())
	tails = append(tails, tail)
	for _, cav := range m.licenses {
		tail = sign(tail, cav)
		tails = append(tails, tail)
	}
	return subtle.ConstantTimeCompare(tail, m.tail) == 1, tails
}

// Head returns copy of macaroon head.
func (m *Macaroon) Head() (head uuid.UUID) {
	head, _ = uuid.FromBytes(m.head.Bytes())
	return
}

// LicenseLen returns the number of Liences this macaroon has.
func (m *Macaroon) LicenseLen() int {
	return len(m.licenses)
}

// Licences returns copy of macaroon caveats.
func (m *Macaroon) Licences() (caveats [][]byte) {
	if len(m.licenses) == 0 {
		return nil
	}
	caveats = make([][]byte, 0, len(m.licenses))
	for _, cav := range m.licenses {
		caveats = append(caveats, append([]byte(nil), cav...))
	}
	return caveats
}
func (m *Macaroon) LicencesUnmark() []*pb.License {
	lisences := make([]*pb.License, 0)
	for _, cav := range m.licenses {
		lisence := pb.License{}
		proto.Unmarshal(cav, &lisence)
		lisences = append(lisences, &lisence)
	}
	return lisences
}

// Tail returns copy of macaroon tail.
func (m *Macaroon) Tail() (tail []byte) {
	if len(m.tail) == 0 {
		return nil
	}
	return append([]byte(nil), m.tail...)
}

// Copy return copy of macaroon.
func (m *Macaroon) Copy() *Macaroon {
	return &Macaroon{
		head:     m.Head(),
		licenses: m.Licences(),
		tail:     m.Tail(),
	}
}
